#include <stdlib.h>
#include <string.h>
#include <apr_pools.h>

#include "svn_pools.h"
#include "svn_error.h"
#include "svn_fs.h"
#include "svn_repos.h"
#include "svn_path.h"
#include "svn_delta.h"
#include "svn_config.h"
#include "svn_props.h"

typedef struct edit_baton
{
  const char *root_path;
  apr_pool_t *pool;
} edit_baton;

typedef struct dir_baton
{
  struct edit_baton *edit_baton;
  const char *full_path;
} dir_baton;


typedef struct file_baton
{
  struct edit_baton *edit_baton;
  const char *path;
} file_baton;

static svn_error_t *
noop_target_revision(void *edit_baton, svn_revnum_t rev, apr_pool_t *pool) {
  printf("[noop_target_rev] %ld\n", rev);
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_open_root(void *edit_baton,
               svn_revnum_t base_revision,
               apr_pool_t *dir_pool,
               void **root_baton)
{
  struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db));
  struct edit_baton *eb = edit_baton;

  db->full_path = eb->root_path;
  db->edit_baton = edit_baton;

  *root_baton = db;
  printf("[noop_open_root] \"%s\" returning 0x%08x\n", eb->root_path,
    (unsigned int)db);
  
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_delete_entry(const char *path,
                  svn_revnum_t revision,
                  void *parent_baton,
                  apr_pool_t *pool)
{
  struct dir_baton *pb = parent_baton;

  printf("[noop_delete_entry] %s %s\n", pb->full_path, path);
  /* Construct the full path of this entry and delete it from the txn. */
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_add_directory(const char *path,
                   void *parent_baton,
                   const char *copyfrom_path,
                   svn_revnum_t copyfrom_revision,
                   apr_pool_t *dir_pool,
                   void **child_baton)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db));

  /* Construct the full path of the new directory */
  db->full_path = svn_path_join(eb->root_path, path, eb->pool);
  printf("[noop_add_dir] %s %s\n", eb->root_path, path);
  db->edit_baton = eb;

  if (copyfrom_path)  /* add with history */
    {
    }
  else  /* add without history */
    ;

  *child_baton = db;
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_open_directory(const char *path,
                    void *parent_baton,
                    svn_revnum_t base_revision,
                    apr_pool_t *dir_pool,
                    void **child_baton)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db));

  /* Construct the full path of the new directory */
  db->full_path = svn_path_join(eb->root_path, path, eb->pool);
  printf("[noop_open_dir] %s\n", db->full_path);
  db->edit_baton = eb;

  *child_baton = db;
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_change_dir_prop(void *parent_baton,
                     const char *name, const svn_string_t *value,
                     apr_pool_t *pool)
{
  struct dir_baton *db = parent_baton;
  struct edit_baton *eb = db->edit_baton;
  printf("[noop_change_dir_prop] %s: '%s'='%s'\n",
          db->full_path, name, value->data);
  /* Construct the full path of this entry and change the property. */
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_close_dir(void *dir_baton, apr_pool_t *pool) {
  printf("[noop_close_dir]\n");
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_absent_dir(const char *path, void *parent_baton, apr_pool_t *pool) {
  printf("[noop_absent_dir] %s\n", path);
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_add_file(const char *path,
              void *parent_baton,
              const char *copyfrom_path,
              svn_revnum_t copyfrom_revision,
              apr_pool_t *file_pool,
              void **file_baton)
{
  struct dir_baton *db = parent_baton;
  struct edit_baton *eb = db->edit_baton;
  struct file_baton *fb = apr_pcalloc(file_pool, sizeof(*fb));

  /* Fill in the file baton. */
  fb->path = svn_path_join(eb->root_path, path, eb->pool);
  printf("[noop_add_file] %s %s @ %ld\n", eb->root_path, path, copyfrom_revision);
  fb->edit_baton = eb;

  if (copyfrom_path)  /* add with history */
    {
    }
  else  /* add without history */
    ;

  *file_baton = fb;
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_open_file(const char *path,
               void *parent_baton,
               svn_revnum_t base_revision,
               apr_pool_t *file_pool,
               void **file_baton)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  struct file_baton *fb = apr_pcalloc(file_pool, sizeof(*fb));

  /* Fill in the file baton. */
  fb->path = svn_path_join(eb->root_path, path, eb->pool);
  fb->edit_baton = eb;
  printf("[noop_open_file] %s %s\n", eb->root_path, path);

  *file_baton = fb;
  if (FALSE && (strcmp(path, "a.txt") == 0 || strstr(path, "a.txt") != NULL)) {
    /* fabricate an error to test error handling/propagation */
    fflush(stdout);
    return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL, NULL/*msg*/);
                            
    
  }
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_window_handler(svn_txdelta_window_t *window,
                    void *baton)
{
  if (window != NULL) {
    const svn_string_t *str = window->new_data;
    if (str != NULL && str->data != NULL) {
      fprintf(stdout, "[noop-editor-window###] ");
      fflush(stdout);
      write(1, str->data, str->len);
      printf("###\n");
    } else {
      printf("[noop-editor-window] NO DATA\n");
    }
  } else {
    printf("[noop-editor-window] NO WINDOW\n");
  }
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_apply_textdelta(void *file_baton,
                     const char *base_checksum,
                     apr_pool_t *pool,
                     svn_txdelta_window_handler_t *handler,
                     void **handler_baton)
{
  struct file_baton *fb = file_baton;
  printf("[noop_apply_textdelta] %s\n", fb->path);
  *handler = noop_window_handler;
  *handler_baton = NULL;
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_change_file_prop(void *file_baton,
                      const char *name, const svn_string_t *value,
                      apr_pool_t *pool)
{
  struct file_baton *fb = file_baton;
  printf("[noop_change_file_prop] %s: '%s'='%s'\n", fb->path, name, value->data);
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_close_file(void *file_baton,
                const char *text_checksum, apr_pool_t *pool) {
  struct file_baton *fb = file_baton;
  printf("[noop_close_file] \"%s\"\n", fb->path);
  return SVN_NO_ERROR;
}


static svn_error_t *
noop_absent_file(const char *path, void *parent_baton,
                 apr_pool_t *pool) {
  printf("[noop_absent_file] %s\n", path);
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_close_edit(void *edit_baton, apr_pool_t *pool) {
  printf("[noop_close_edit]\n");
  return SVN_NO_ERROR;
}

static svn_error_t *
noop_abort_edit(void *edit_baton, apr_pool_t *pool) {
  printf("[noop_abort_edit]\n");
  return SVN_NO_ERROR;
}


/*---------------------------------------------------------------*/
svn_error_t *
init_svnj_noop_editor(const svn_delta_editor_t **editor,
            void **edit_baton,
            const char *path,
            apr_pool_t *pool)
{
  svn_delta_editor_t *my_editor;
  struct edit_baton *my_edit_baton;

  /* Set up the editor. */
  /*my_editor = svn_delta_default_editor(pool); */
  my_editor = apr_palloc(pool, sizeof(svn_delta_editor_t));
  memset(my_editor, 0, sizeof(svn_delta_editor_t));
  
  my_editor->set_target_revision = noop_target_revision;
  my_editor->open_root           = noop_open_root;
  my_editor->delete_entry        = noop_delete_entry;
  my_editor->add_directory       = noop_add_directory;
  my_editor->open_directory      = noop_open_directory;
  my_editor->change_dir_prop     = noop_change_dir_prop;
  my_editor->close_directory     = noop_close_dir;
  my_editor->absent_directory    = noop_absent_dir;
  my_editor->add_file            = noop_add_file;
  my_editor->open_file           = noop_open_file;
  my_editor->apply_textdelta     = noop_apply_textdelta;
  my_editor->change_file_prop    = noop_change_file_prop;
  my_editor->close_file          = noop_close_file;
  my_editor->absent_file         = noop_absent_file;
  my_editor->close_edit          = noop_close_edit;
  my_editor->abort_edit          = noop_abort_edit;

  /* Set up the edit baton. */
  my_edit_baton = apr_pcalloc(pool, sizeof(*my_edit_baton));
  my_edit_baton->root_path = apr_pstrdup(pool, path);
  my_edit_baton->pool = pool;
  printf("[get_noop_editor] \"%s\"\n", my_edit_baton->root_path);
  *editor = my_editor;
  *edit_baton = my_edit_baton;

  return SVN_NO_ERROR;
}
